#!/usr/bin/python3
# -*- coding: utf-8; tab-width: 4; indent-tabs-mode: t -*-

import os
import re
import gstage4
import wstage4
import strict_hwcfg
from fm_util import Util
from fm_util import PhysicalDiskMounts
from fm_param import FmConst
from stage4_gentoo import Stage4GentooBuilder
from stage4_mswin import Stage4WinXpBuilder
from stage4_mswin import Stage4Win7Builder
from helper_dyncfg import DynCfgModifier
from helper_pkg_warehouse import CloudOverlayDb
from helper_assist_disk import AssistOsBuilder
from helper_assist_disk import AssistDiskBuilder


class FmLoggingManager:

    def __init__(self, param):
        self.param = param

    def getLogCfg(self):
        pass

    def setLogCfg(self):
        pass


class FmOtherHostManager:

    def __init__(self, param):
        self.param = param
        self.infoPrinter = self.param.infoPrinter
        self.stage4GentooCacheDirDict = {
            "amd64": FmConst.stage4GentooAmd64CacheDir,
            "arm64": FmConst.stage4GentooArm64CacheDir,
        }

    def buildAssistDisk(self, devPath, bWithStage4, bUseCache):
        diskFsUuid = Util.fatGenerateRandomFsUuid()
        if bWithStage4:
            diskMinSizeInGb = 15
        else:
            diskMinSizeInGb = 3

        # check
        if devPath.endswith(".iso"):
            pass
        elif devPath.endswith(".img"):
            pass
        elif re.fullmatch("/dev/sd.*", devPath) is not None:
            if not Util.isBlkDevUsbStick(devPath):
                raise Exception("device %s does not seem to be an usb-stick." % (devPath))
            if Util.getBlkDevSize(devPath) < diskMinSizeInGb * 1024 * 1024 * 1024:
                raise Exception("device %s needs to be at least %d GB." % (devPath, diskMinSizeInGb))
            if len(PhysicalDiskMounts.find_entries_by_filter(lambda x: x.device.startswith(devPath) and Util.devPathDiskOrPartitionToDisk(x.device) == devPath)) > 0:
                raise Exception("device %s or any of its partitions is already mounted, umount it first." % (devPath))
        elif re.fullmatch("/dev/sr.*", devPath) is not None:
            pass
        else:
            raise Exception("target is not supported")

        # prepare
        self.infoPrinter.printInfo(">> Preparing...")
        machine = None
        wdirAssistOsAmd64 = None
        wdirAssistOsArm64 = None
        wdirStage4GentooAmd64 = None
        wdirStage4GentooArm64 = None
        wdirStage4WinXp = None
        wdirStage4Win7 = None
        if True:
            self.param.sysChecker.init(False, False)
            self.param.sysChecker.basicCheck(repositoryOverlayCheck=True)

            DynCfgModifier.update()

            machine = strict_hwcfg.probe()

            # always use cache directory, (not bUseCache) means "don't reuse cache directory content and refresh cache directory"
            os.makedirs(FmConst.stage4CacheDir, exist_ok=True)
            if bUseCache:
                wdirAssistOsAmd64 = self.__getStage4(FmConst.stage4AssistOsAmd64CacheDir, "amd64", gstage4.WorkDir)
                wdirAssistOsArm64 = self.__getStage4(FmConst.stage4AssistOsArm64CacheDir, "arm64", gstage4.WorkDir)
                if bWithStage4:
                    wdirStage4GentooAmd64 = self.__getStage4(FmConst.stage4GentooAmd64CacheDir, "amd64", gstage4.WorkDir)
                    wdirStage4GentooArm64 = self.__getStage4(FmConst.stage4GentooArm64CacheDir, "arm64", gstage4.WorkDir)
                    wdirStage4WinXp = self.__getStage4(FmConst.stage4WinXpCacheDir, None, wstage4.WorkDir)
                    wdirStage4Win7 = self.__getStage4(FmConst.stage4Win7CacheDir, None, wstage4.WorkDir)

            print("")

        # update overlay database if needed
        self.infoPrinter.printInfo(">> Updating overlay database...")
        overlayDb = CloudOverlayDb()
        overlayDb.update()
        print("")

        # create neccessary builders
        assistOsBuilder = AssistOsBuilder(self.infoPrinter, overlayDb, machine, True, FmConst.assistDiskName)
        stage4GentooBuilder = None
        stage4WinXpBuilder = None
        stage4Win7Builder = None
        if bWithStage4 and (wdirStage4GentooAmd64 is None or wdirStage4GentooArm64 is None):
            stage4GentooBuilder = Stage4GentooBuilder(self.infoPrinter, overlayDb, machine)
        if bWithStage4 and wdirStage4WinXp is None:
            stage4WinXpBuilder = Stage4WinXpBuilder(self.infoPrinter, self.param.tmpDirOnHdd)
        if bWithStage4 and wdirStage4Win7 is None:
            stage4Win7Builder = Stage4Win7Builder(self.infoPrinter, self.param.tmpDirOnHdd)

        # download files if neccessary
        if wdirAssistOsAmd64 is None or wdirAssistOsArm64 is None or stage4GentooBuilder is not None or stage4WinXpBuilder is not None or stage4Win7Builder is not None:
            with self.infoPrinter.printInfoAndIndent(">> Downloading files..."):
                if stage4GentooBuilder is not None:
                    stage4GentooBuilder.downloadFiles(True)
                if stage4WinXpBuilder is not None:
                    stage4WinXpBuilder.downloadFiles(True)
                if stage4Win7Builder is not None:
                    stage4Win7Builder.downloadFiles(True)
                if assistOsBuilder is not None:
                    assistOsBuilder.downloadFiles(True)
                print("")

        if bWithStage4:
            if wdirStage4GentooAmd64 is not None:
                self.infoPrinter.printInfo(">> Using cache for %s (%s) stage4..." % (Util.getOsName("gentoo"), Util.getArchName("amd64")))
            else:
                with self.infoPrinter.printInfoAndIndent(">> Building Gentoo Linux (%s) stage4..." % (Util.getArchName("amd64"))):
                    wdirStage4GentooAmd64 = stage4GentooBuilder.build("amd64", FmConst.stage4GentooAmd64CacheDir)
            print("")

            # if stage4GentooArm64 is not None:
            #     self.infoPrinter.printInfo(">> Using cache for Gentoo Linux (%s) stage4..." % (Util.getArchName("arm64")))
            # else:
            #     with self.infoPrinter.printInfo(">> Building Gentoo Linux (%s) stage4..." % (Util.getArchName("arm64"))):
            #         stage4GentooArm64 = stage4GentooBuilder.build("arm64", FmConst.stage4GentooArm64CacheDir)
            # print("")

            if wdirStage4WinXp is not None:
                self.infoPrinter.printInfo(">> Using cache for %s (%s) stage4..." % (Util.getOsName("mswin_xp"), Util.getArchName("i386")))
            else:
                with self.infoPrinter.printInfoAndIndent(">> Building %s (%s) stage4..." % (Util.getOsName("mswin_xp"), Util.getArchName("i386"))):
                    wdirStage4WinXp = stage4WinXpBuilder.build(FmConst.stage4WinXpCacheDir)
            print("")

            if wdirStage4Win7 is not None:
                self.infoPrinter.printInfo(">> Using cache for %s (%s) stage4..." % (Util.getOsName("mswin_7"), Util.getArchName("amd64")))
            else:
                with self.infoPrinter.printInfoAndIndent(">> Building %s (%s) stage4..." % (Util.getOsName("mswin_7"), Util.getArchName("amd64"))):
                    wdirStage4Win7 = stage4Win7Builder.build(FmConst.stage4Win7CacheDir)
            print("")

        if wdirAssistOsAmd64 is not None:
            self.infoPrinter.printInfo(">> Using cache for %s (%s)..." % (FmConst.assistDiskName, Util.getArchName("amd64")))
            assistOsBuilder.updateInitramfs(diskFsUuid, wdirAssistOsAmd64)
        else:
            with self.infoPrinter.printInfoAndIndent(">> Building %s (%s)..." % (FmConst.assistDiskName, Util.getArchName("amd64"))):
                wdirAssistOsAmd64 = gstage4.WorkDir(FmConst.stage4AssistOsAmd64CacheDir, reset=True)
                assistOsBuilder.build("amd64", diskFsUuid, wdirAssistOsAmd64)
        print("")

        # if assistOsArm64 is not None:
        #     self.infoPrinter.printInfo(">> Using cache for %s (%s)..." % (FmConst.assistDiskName, Util.getArchName("amd64")))
        # else:
        #     with self.infoPrinter.printInfo(">> Building %s (%s)..." % (FmConst.assistDiskName, Util.getArchName("arm64"))):
        #         assistOsArm64 = gstage4.WorkDir(FmConst.stage4AssistOsArm64CacheDir, reset=True)
        #         assistOsBuilder.build("arm64", assistOsArm64)
        # print("")

        # export to target
        if devPath.endswith(".iso"):
            self.infoPrinter.printInfo(">> Creating %s..." % (devPath))
        elif devPath.endswith(".img"):
            self.infoPrinter.printInfo(">> Creating %s..." % (devPath))
        elif re.fullmatch("/dev/sd.*", devPath) is not None:
            self.infoPrinter.printInfo(">> Installing into USB stick %s..." % (devPath))
        elif re.fullmatch("/dev/sr.*", devPath) is not None:
            self.infoPrinter.printInfo(">> Burning CD in %s..." % (devPath))
        else:
            assert False
        assistDiskBuilder = AssistDiskBuilder(diskFsUuid, FmConst.assistDiskLabel, FmConst.assistDiskName)
        assistDiskBuilder.addAssistOs(wdirAssistOsAmd64)
        # assistDiskBuilder.addAssistOs(assistOsArm64)                      # FIXME
        if bWithStage4:
            assistDiskBuilder.addStage4("gentoo", wdirStage4GentooAmd64)
            # assistDiskBuilder.addStage4(stage4GentooArm64)                # FIXME
            assistDiskBuilder.addStage4("mswin_xp", wdirStage4WinXp)
            assistDiskBuilder.addStage4("mswin_7", wdirStage4Win7)
        if devPath.endswith(".iso"):
            assistDiskBuilder.exportToIsoFile(devPath)
        elif devPath.endswith(".img"):
            assistDiskBuilder.exportToDiskImageFile(devPath, diskMinSizeInGb * 1024 * 1024 * 1024)
        elif re.fullmatch("/dev/sd.*", devPath) is not None:
            assistDiskBuilder.exportToUsbStick(devPath)
        elif re.fullmatch("/dev/sr.*", devPath) is not None:
            assert False
        else:
            assert False
        print("")

    def newHost(self, osType, hostname, devPath, bUseCache):
        # prepare
        self.infoPrinter.printInfo(">> Preparing...")
        machine = None
        wdirStage4Gentoo = None
        wdirStage4WinXp = None
        wdirStage4Win7 = None
        if True:
            self.param.sysChecker.init(False, False)
            self.param.sysChecker.basicCheck(repositoryOverlayCheck=True)

            DynCfgModifier.update()

            machine = strict_hwcfg.probe()

            # always use cache directory, (not bUseCache) means "don't reuse cache directory content and refresh cache directory"
            os.makedirs(FmConst.stage4CacheDir, exist_ok=True)
            if bUseCache:
                if osType == "gentoo":
                    wdirStage4Gentoo = {
                        "amd64": self.__getStage4(self.stage4GentooCacheDirDict["amd64"], "amd64", gstage4.WorkDir),
                        "arm64": self.__getStage4(self.stage4GentooCacheDirDict["arm64"], "arm64", gstage4.WorkDir),
                    }
                elif osType == "mswin_xp":
                    wdirStage4WinXp = self.__getStage4(FmConst.stage4WinXpCacheDir, None, wstage4.WorkDir)
                elif osType == "mswin_7":
                    wdirStage4Win7 = self.__getStage4(FmConst.stage4Win7CacheDir, None, wstage4.WorkDir)
                else:
                    assert False

            print("")

        # check and get target information
        arch = None
        with self.infoPrinter.printInfoAndIndent(">> Accessing target host..."):
            # FIXME
            if osType == "gentoo":
                wdirStage4Gentoo = wdirStage4Gentoo[arch]
            elif osType == "mswin_xp":
                pass
            elif osType == "mswin_7":
                pass
            else:
                assert False
            print("")

        if osType == "gentoo":
            self._buildAndFlushGentoo(machine, arch, wdirStage4Gentoo)
        elif osType == "mswin_xp":
            self._buildAndFlushWinXp(machine, arch, wdirStage4WinXp)
        elif osType == "mswin_7":
            self._buildAndFlushWin7(machine, arch, wdirStage4Win7)
        else:
            assert False

        print("Done. Host \"%s\" should be usable after rebooting finishes." % (hostname))

    def _buildAndFlushGentoo(self, machine, arch, wdirStage4Gentoo, storageLayoutName):
        if wdirStage4Gentoo is not None:
            # use cache directly
            self.infoPrinter.printInfo(">> Using cache for Gentoo Linux (%s) stage4..." % (Util.getArchName(arch)))
            print("")
        else:
            # update overlay database
            self.infoPrinter.printInfo(">> Updating overlay database...")
            overlayDb = CloudOverlayDb()
            overlayDb.update()
            print("")

            stage4GentooBuilder = Stage4GentooBuilder(self.infoPrinter, overlayDb, machine)

            # download files
            with self.infoPrinter.printInfoAndIndent(">> Downloading files..."):
                if stage4GentooBuilder is not None:
                    stage4GentooBuilder.downloadFiles(False)
                print("")

            # build
            with self.infoPrinter.printInfoAndIndent(">> Building Gentoo Linux (%s) stage4..." % (Util.getArchName(arch))):
                wdirStage4Gentoo = stage4GentooBuilder.build(arch, self.stage4GentooCacheDirDict[arch])
            print("")

        self.infoPrinter.printInfo(">> Creating storage layout (%s)..." % (storageLayoutName))

        self.infoPrinter.printInfo(">> Transfering stage4...")

        self.infoPrinter.printInfo(">> Doing final setup...")

    def _buildAndFlushWinXp(self, machine, arch, wdirStage4WinXp):
        assert False

    def _buildAndFlushWin7(self, machine, arch, wdirStage4Win7):
        assert False

    @staticmethod
    def __getStage4(path, arch, workDirClass):
        if os.path.exists(path):
            wdir = workDirClass(path)
            if wdir.is_build_finished():
                if arch is None or wdir.get_arch() == arch:
                    return wdir
        return None
